home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Information / Programming / Writing System Extensions / Writing System Extensions 1⁄3 next >
Encoding:
Text File  |  1994-12-03  |  24.7 KB  |  541 lines  |  [TEXT/R*ch]

  1. Answers to Frequently Asked Questions about writing System Extensions 
  2. on the Macintosh Computer. Version 1.0.1 11/94
  3.  
  4. This document is Copyright © 1994 by Brian Stern.
  5. I can be contacted by email at <Jaeger@fquest.com> on Internet.
  6.  
  7. The purpose of this FAQ list is to provide information on the writing 
  8. of system extensions.  This arcane art is difficult to learn and the 
  9. existing information on the subject is spread around in various 
  10. places.  Hopefully the information here will help new and old INIT 
  11. writers to write better INITs in less time.
  12.  
  13. The questions in this document are broken into two sections: System 
  14. Extensions and Trap Patches.  While most extensions contain trap 
  15. patches these subjects seemed different so I have separated them.
  16.  
  17. FAQ lists like this one are an integral part of Usenet.  When I 
  18. started reading Usenet news I assure you that I knew nothing about 
  19. writing extensions.  I learned by posting questions to 
  20. comp.sys.mac.programmer and by reading other's questions and 
  21. responses.  Let me take this space to give you a few tips on phrasing 
  22. of questions to newsgroups.
  23.  
  24. When asking a question try to make the title as descriptive as 
  25. possible.  Most people don't have time to read every question posted 
  26. and will ignore posts whose titles are unclear or meaningless.  Don't 
  27. post questions titled 'HELP, My program doesn't work', or worse 
  28. 'URGENT NEED HELP with program'.  I'm sure it's urgent to you, but 
  29. not to me.  Always indicate that you're asking a questing by 
  30. including a (you guessed it) question mark.  Consider these titles: 
  31. 'Programmers make big salaries' and 'Programmers make big salaries?'  
  32. Don't waste people's time with the former when you mean the latter.  
  33. Another way to indicate a question is like this: '[Q] Programmers 
  34. make big salaries?'.  Try to include one of the words 'who, what, 
  35. when, where, why, or how' in the titles of your questions.  Your 
  36. questions are much more likely to be answered by someone who actually 
  37. knows the answer if your title is clear.
  38.  
  39. The code samples in this document were developed with Think C.  You 
  40. may need to make some changes to use them with other development 
  41. systems.  I've used inline assembly in many of the code samples.  To 
  42. my eye this makes things more clear since it's obvious what's going 
  43. on.  Not everyone agrees with this and not all development 
  44. environments provide inline assembly.  It's possible to rewrite most 
  45. or all of this code by use of inline functions.  If you're writing in 
  46. Pascal or using CodeWarrior you'll have to do it that way.
  47.  
  48. You'll be able to find information on general questions of Mac 
  49. programming in the FAQ list maintained by Jon Watte at: 
  50. ftp://nada.kth.se/pub/hacks/mac-faq/CSMP_PD_FAQ
  51.  
  52. If you have questions about writing extensions that aren't addressed 
  53. in this document or any comments about it feel free to send them to 
  54. me.  If I know the answer or can find it out it may find its way into 
  55. a later version.
  56.  
  57. This document may be distributed freely as long as no changes are 
  58. made to it.  It may not be distributed in ways in which the user must 
  59. pay for the document, other than reasonable download costs or costs 
  60. for the medium, without the consent of the author.
  61.  
  62. Thanks to the following people who provided sample code and made 
  63. constructive comments: Pete Gontier, Dair Grant, Chelly Green, Devon 
  64. Hubbard, Peter Lewis, Jim Walker, Jim Wintermyre.
  65.  
  66. ---------------------------------------------------------------------
  67. System Extension Writing FAQ Outline:
  68.  
  69. System Extensions:
  70.  
  71. [1] What is an INIT, exactly?
  72. [2] Should I write an INIT?
  73. [3] Can I write an INIT that makes the application menu into a 
  74. hierarchical menu?
  75. [4] Do I need to know assembler to write an INIT?
  76. [5] Can you show me a sample INIT?
  77. [6] Can I have global variables in my INIT?
  78. [7] How do I debug an INIT?
  79. [8] How do I write a Control Panel-INIT combination?
  80. [9] How do I capture keystrokes?
  81. [10] How can my extension get time periodically?
  82. [11] How should an INIT manage memory?
  83. [12] How do I get my INIT to turn itself off?
  84. [13] How do I get my INIT to show its icon like all the other cool 
  85. INITs do?
  86. [14] How do I show a dialog from my INIT?
  87. [15] How do I maintain compatibility with future systems?
  88. [16] Any tips for INIT writing?
  89.  
  90. Trap Patches:
  91.  
  92. [17] What exactly is a trap patch?
  93. [18] What's the difference between a head patch and a tail patch?
  94. [19] How do I patch a trap?
  95. [20] How do I patch a register-based trap?
  96. [21] Can you show me a tail patch?
  97. [22] How do I patch a selector-based trap?
  98. [23] How do I patch a trap on the PPC?
  99. [24] Can I write a fat trap?
  100. [25] Tips?
  101. [26] What other sources of information are available?
  102.  
  103. ---------------------------------------------------------------------
  104.  
  105. [1] What is an INIT, exactly?
  106.  
  107. An INIT is a type of code resource that is loaded into memory and 
  108. executed during the startup process.  They're called INITs because 
  109. they're resources of type 'INIT'.  INITs load at the end of the 
  110. startup process, after the hardware checks have been done and the 
  111. system has been started.  The span of time during which INITs load 
  112. and execute is known as INIT time.  Applications don't load until 
  113. after INIT time and in most cases the first application to load is 
  114. the Finder.
  115.  
  116. Because they load before all applications, INITs can modify the 
  117. system in a way that affects all applications.  They can add new 
  118. functionalities and modify the way in which many processes occur on 
  119. the Mac.  Apple often supplies new additions and updates to system 
  120. software as INITs.  This includes things such as the Drag-and-Drop 
  121. Manager, Thread Manager, Speech Manager, and Sound Manager 3.0.  At 
  122. some point these new features are rolled into a new software release 
  123. and eventually they are included in new ROMs, but they all start life 
  124. as INITs.
  125.  
  126. Apple recommends that the term 'system extension' be used when 
  127. communicating with non-programmers.  This name carries the 
  128. implication that they extend the functionality of the system.  You'll 
  129. see the terms 'INIT', 'extension', and 'system extension' used to 
  130. mean the same thing throughout this document.
  131.  
  132. Extensions are loaded in a defined order.  Resources of type 'INIT' 
  133. can be found in files of type 'INIT' (System Extension), 'cdev' 
  134. (Control Panel), 'RDEV' (Chooser Device), 'appe' (application 
  135. extension), and 'scri' (script system extensions).  In order to be 
  136. loaded, the INIT resource must be in one of these file types in one 
  137. of several locations in the System Folder.  
  138.  
  139. Under system 7 INITs are loaded first from the Extensions Folder in 
  140. alphabetical order.  INITs present in script system extension files 
  141. (filetype 'scri') load before INITs present in system extension files 
  142. (filetype 'INIT').  Next, any INITs in the Control Panels folder are 
  143. loaded in alphabetical order.  Any INITs present at the root level of 
  144. the System Folder are loaded after that.  This order of loading is 
  145. important if an extension is dependent on the presence of another 
  146. extension.  For example, if an INIT resource in an INIT file named 
  147. 'SpeakAll' is dependent on the presence of the 'Speech Manager' 
  148. extension, it either needs to have its name changed to something that 
  149. sorts after 'Speech Manager' or it has to be located in the Control 
  150. Panels folder, since extensions in the Control Panels folder load 
  151. after extensions in the Extensions Folder. 
  152.  
  153. In system 6 the Extensions Folder and Control Panels Folders don't 
  154. exist.  Extensions are simply loaded in alphabetical order from the 
  155. root level of the System Folder.
  156.  
  157. --
  158. [2] Should I write an INIT?
  159.  
  160. System extensions are not easy to write.  If this is your first 
  161. attempt at Mac programming, the answer to this question is NO.  Many 
  162. experienced Mac programmers have never written one and don't intend 
  163. to.  
  164.  
  165. There are a number of other ways to add functionality without writing 
  166. an extension.  If you want to add functionality to a single 
  167. application then think about writing an FKEY.  These are invoked by 
  168. hitting cmd-shift-number and perform an action at that time only.  
  169. The standard screen shot (cmd-shift-3) is one example.
  170.  
  171. Another user-invokable means of adding functionality is plug-in 
  172. modules.  A number of commercial software packages support plug-ins 
  173. that can add functionality in a straightforward manner.  This 
  174. includes PhotoShop, Quark Express, Hypercard and others.
  175.  
  176. Another means of adding functionality is the use of a background-only 
  177. application, AKA faceless-background application (fba).  Fbas are 
  178. applications without a user interface.  One type of fba is known as 
  179. an application extension.  These are applications whose resource 
  180. files are of type 'appe'.  They are placed in the extensions folder 
  181. (or the Startup Items Folder) and are started up after INIT time.  
  182. They can communicate with other processes by Apple Events and by 
  183. Gestalt selectors.  If you don't need to patch a trap then an fba may 
  184. be the way to add functionality to the system.  Since an fba runs as 
  185. a normal process there are some things that it can do that system 
  186. extensions cannot do (or at least cannot do safely), like launch 
  187. applications and send and receive Apple Events.  An INIT resource in 
  188. the resource fork of an application extension in the Extensions 
  189. Folder will load normally at INIT time.  See issue 9 of develop and 
  190. the Tech Note PS 2 - Background-Only Applications for more info.
  191.  
  192. Also consider an application that is started up by being placed in 
  193. the startup items folder.  The Screen Saver 'Dark Side of the Mac' is 
  194. written in this way.  Screen Savers are traditionally written as 
  195. extensions because extensions can have access to the mouse location 
  196. and all keyboard events.  However an intelligently written 
  197. application can do the same and be more compatible.  
  198.  
  199. If none of these things seems like it will work for you and you are 
  200. thinking something like 'I want X to happen every time a resource is 
  201. loaded', or otherwise add to or replace the system's functionality in 
  202. some way then an extension is probably what you need.
  203.  
  204. Remember that when developing system extensions you will be working 
  205. without a net.
  206.  
  207. --
  208. [3] Can I write an INIT that makes the application menu into a 
  209. hierarchical menu?
  210.  
  211. Every couple of weeks someone posts a message on 
  212. comp.sys.mac.programmer entitled 'Neat idea for an init'.  These 
  213. posts go on to describe some non-programmer's idea of a great INIT.  
  214. Invariably these posts fall into two groups: those that have already 
  215. been done, and those that should never be done.  Think long and hard 
  216. about the design of an extension before starting to write it.  
  217. Consider your target audience.  If it's only yourself then you can do 
  218. what you want.  My first couple of extensions were just tests to see 
  219. if I could actually do it.  The answer to the above question is: 
  220. Maybe you can but probably you shouldn't.
  221.  
  222. --
  223. [4] Do I need to know assembler to write an extension?
  224.  
  225. Yes.  Strictly speaking, the simplest extension that just beeps at 
  226. startup and doesn't hang around past INIT time requires no assembler.  
  227. However, any extension that does anything useful will require some 
  228. assembler in order to patch a trap or install a jGNEFilter.  The 
  229. extension can be written mostly in a high level language like C or 
  230. Pascal, but there will be bits and pieces that need to be written in 
  231. assembler to keep the stack happy or to access parameters passed in 
  232. registers.  Hopefully there will be enough sample code in this 
  233. document to get you on your way if your assembler is weak.
  234.  
  235. You will often need to inspect the disassembled source of your 
  236. extension.  If your development environment doesn't allow this the 
  237. ResEdit Code Viewer will also allow you to inspect code resources.  
  238. It can be found at ftp://ftp.apple.com/dts/mac/tools/resedit/resedit-
  239. extensions.hqx.
  240.  
  241. --
  242. [5] Can you show me a sample INIT?
  243.  
  244. Here's the code for the 'Hello World' of INITs.
  245.  
  246. void main(void)
  247. {
  248.    SysBeep( 5 );
  249. }
  250.  
  251. Not very complicated is it?  There are a number of additional things 
  252. that are required to make this work:
  253.  
  254. 1) Set the project type to code resource.
  255. 2) Set the resource type to 'INIT'.
  256. 3) Set the resource ID to something (>= 128), and set the resource 
  257. name if desired.
  258. 4) Set the resource attributes to 'System Heap'.
  259. 5) Set the resource attributes to 'Locked'.
  260. 6) Set the filetype of the built code resource file to 'INIT' with 
  261. any creator signature.
  262. 7) Set the filename of the built code resource file to 'Hello World'.
  263. 8) Include a 'sysz' resource indicating a size of 100K.
  264.  
  265. Let me explain what each of these things does.  
  266.  
  267. 1)  Because you're building a standalone code resource your compiler 
  268. needs to know this.  This is how the compiler knows to use A4 
  269. addressing, rather than the A5 addressing used in applications.
  270.  
  271. 2)  As mentioned above, extensions are code resources of type 'INIT'.  
  272.  
  273. 3)  Like any other resources they have IDs and can have names.  
  274.  
  275. 4)  Setting the system heap flag places the code resource in the 
  276. system heap.  While not strictly required for this sample, in most 
  277. other extensions we will want the code resource to remain in the 
  278. system heap so this flag must be set.  During the process that loads 
  279. and executes INITs a small heap is created.  Any memory allocation 
  280. done by the INIT will, by default occur in this heap.  Also any 
  281. resources read into memory will, by default, go into this heap.  If 
  282. the system heap flag isn't set for the INIT itself then the INIT will 
  283. be loaded into this temporary heap.  When the INIT exits the heap is 
  284. disposed and anything in it is lost.  If an INIT intends to stay 
  285. around past INIT time then it must have the system heap flag set.
  286.  
  287. 5)  The code resource should never move in memory so the Locked bit 
  288. is set.  When a resource with the Locked bit is loaded into memory it 
  289. is loaded as low in the heap as possible.  This is what we want since 
  290. the INIT resource will never be unlocked.  It is poor design to not 
  291. set the Locked bit and to rely on the INIT to lock itself.  In that 
  292. case the INIT will not be loaded low in the system heap.  This isn't 
  293. fatal but will lead to fragmentation of the system heap.  
  294.  
  295. Some INITs try to use MoveHHi/HLock to move themselves or associated 
  296. resources high in the system heap.  MoveHHi is disabled for the 
  297. system heap.  Since the system heap is dynamically sizable the 
  298. concept of the top of the heap is invalid.  For this reason MoveHHi 
  299. does nothing when the handle being referred to is within the system 
  300. heap.  
  301.  
  302. 6)  The filetype of 'INIT' gives you the generic extension icon, and 
  303. of course allows the extension to load and execute at INIT time.
  304.  
  305. 7)  Set the file name to something like 'Hello World'.
  306.  
  307. 8)  A 'sysz' resource indicates to the INIT-loading code that your 
  308. INIT requires a contiguous block of memory of the indicated size.  
  309. The system heap will be expanded if necessary to make certain that a 
  310. block of the requested size is available.  If no 'sysz' recource is 
  311. included the system assumes a block of 16K is sufficient.  The format 
  312. of the 'sysz' resource is simply a long integer indicating the 
  313. blocksize.  You can create one with ResEdit, or copy one from another 
  314. INIT and edit it with the hex editor.  This sample extension requires 
  315. more than 16K because SysBeep allocates a sound channel and because 
  316. you don't know what the size of the 'snd ' resource is for the system 
  317. alert sound.  If no 'sysz' resource has been included this INIT 
  318. sometimes doesn't work because there isn't enough room to allocate 
  319. the sound channel.
  320.  
  321. Once these things have been done you can build the code resource and 
  322. drag its file to the System Folder.  The Finder should place it in 
  323. the Extensions Folder for you and it should have the generic 
  324. extension icon.  When you restart you will hear the beep when the 
  325. extension is loaded.
  326.  
  327. Every extension must have a routine called 'main'.  When the INIT 
  328. resource is loaded into memory during INIT time the system jumps to 
  329. the beginning of the code resource.  The header of the code resource 
  330. will normally contain a branch instruction that branches to the main 
  331. routine.  This routine does whatever it needs to do and then returns.  
  332. In this 'Hello World' extension all that the main routine does is 
  333. call SysBeep.  In more substantial extensions the main routine will 
  334. do things like patch traps, install gestalt selectors, and load 
  335. resources into memory.
  336.  
  337. --
  338. [6] Can I have global variables in my INIT?
  339.  
  340. Yes you can.  The Think environment and the CodeWarrior environment 
  341. use A4-based addressing for global variables in code resources.  The 
  342. base address of the extension is found in register A0 on entry to the 
  343. extension, and routines are provided to save this value and to set up 
  344. and restore register A4 when the INIT is entered later.  (In fact the 
  345. standard header installed by Think C in code resources loads the 
  346. address of the code resource into A0 by use of pc-relative 
  347. addressing.)  See the A4-Addressing section of your development 
  348. environment's manual for more information.  
  349.  
  350. When writing extensions with the MPW compilers you may need to use an 
  351. A5-based method for accessing global variables.  See the Apple tech 
  352. note 'StandAlone Code, ad nauseam' (#256) for more information on 
  353. this method.  ftp://ftp.apple.com/dts/mac/tn/platforms.tools.pt/pt-
  354. 35-stand-alone-code.hqx 
  355.  
  356. and 
  357.  
  358. "Another take on Globals in Standalone Code", Keith Rollin
  359. ftp://ftp.apple.com/dts/mac/docs/develop/develop.12.code/globals-in-
  360. standalone-code.hqx
  361.  
  362. Here is some sample code using the Think C routines to illustrate 
  363. this:
  364.  
  365. /****Main*****************************************************/
  366. void 
  367. main(void)        //Main entry point of the extension
  368. {
  369.    void     *pToMe;
  370.    Boolean  initedOK;
  371.  
  372.    pToMe = GetA0();        //Save our address locally
  373.  
  374.    RememberA0();        //Save our base address
  375.    SetUpA4();           //Set up A4-based addressing
  376.  
  377.    initedOK = InitAll();      //Patch traps etc.
  378.    if ( initedOK )
  379.    {
  380.                   //Make sure we stay around
  381.       DetachResource( RecoverHandle( pToMe) );  
  382.    }
  383.  
  384.    RestoreA4();         //Reset A4 to its value on entry
  385. }
  386.  
  387. This code sample also illustrates one method extensions can use to 
  388. remain in memory after INIT time.  Since extensions are code 
  389. resources they will be removed from memory when their resource files 
  390. are closed.  This happens when the main routine exits.  To avoid 
  391. this, DetachResource must be called with the handle to the code 
  392. resource.  One common mistake with this is not having the code 
  393. resource marked system heap.  If it's not marked system heap it will 
  394. be lost when the temporary heap is destroyed, whether it was detached 
  395. or not.  The GetA0 routine used above is defined in the tips section 
  396. farther down in this document.  Here are a few other methods for an 
  397. extension to detach itself:
  398.  
  399. void 
  400. main(void)     //Another method
  401. {
  402.    asm
  403.    {
  404.       RecoverHandle     //A0 already holdss our address
  405.       move.L   A0, -(A7)   //DetachResource is stack-based
  406.       DetachResource    //so push A0 onto the stack
  407.    }
  408. }
  409.  
  410. void 
  411. main(void)     //This method uses no assembler
  412. {
  413.    DetachResource( Get1Resource( 'INIT', kOurResID ) );
  414. }
  415.  
  416. --
  417. [7] How do I debug an INIT?
  418.  
  419. Debugging is generally the most time consuming, difficult, and all-
  420. around pain-in-the-neck part of producing good code.  That goes 
  421. double for INITs.  Unfortunately many parts of INITs must be debugged 
  422. with low level debuggers.  The low level debuggers that I know about 
  423. are MacsBug, TMON, and Jasik's Debugger.  MacsBug is free and is 
  424. available at ftp://ftp.apple.com/dts/mac/tools/macsbug/macsbug-6-
  425. 5d6.hqx.  The two other low level debuggers are commercialware.  
  426. Jasik's debugger has the ability to do source level debugging of code 
  427. resources, like system extensions.
  428.  
  429. In most cases, your low level debugger of choice will load before 
  430. INIT time so it can be used to debug initialization of INITs.  One 
  431. exception to this is Jasik's Debugger, which loads in two parts.  One 
  432. of these is an extension that must load before your extension.  
  433.  
  434. The debugger can be invoked by calling the Debugger() or DebugStr() 
  435. traps.  Placing these trap calls in your code will drop you into the 
  436. low level debugger so that you can step through your code and inspect 
  437. registers or memory as needed.
  438.  
  439. One useful technique is to take advantage of Macsbug's ability to 
  440. process commands after a semicolon in a DebugStr call.  The following 
  441. function can display information that you would otherwise have to 
  442. hunt down using hex offsets:
  443.  
  444. pascal void SomeFunction (arguments)
  445. {
  446.     // ...
  447.  
  448.     asm { MOVE.L fooP, A0 }      // fooP can be any type
  449.     asm { MOVE.L sizeof(*fooP), D0 }
  450.     DebugStr ("\p ; dm rA0 rD0");   // dump (*fooP) in hex
  451.  
  452.     // ...
  453. }
  454.  
  455. If you haven't placed Debugger() calls in your code then you will 
  456. have to set a breakpoint in order to step through any trap patches or 
  457. other parts of your extension.  Here are a couple of methods to do 
  458. that.  If you have patched a trap, say GetResource, if you drop into 
  459. MacsBug and type 'il GetResource' MacsBug will begin to disassemble 
  460. at your patch.  You can then set a breakpoint by typing 'br 
  461. GetResource' or br theaddress' where theaddress is the address in hex 
  462. of your patch or some part of it.  Type 'brc' when you want to clear 
  463. all breakpoints.  Using the A-trap commands will also work.  'atb 
  464. GetResource' will set a break and 'atc' will clear all A-trap 
  465. breakpoints.
  466.  
  467. If you want to set a breakpoint in some other part of your extension, 
  468. say in a jGNEFilter, then you need another way of finding its 
  469. location in memory.  In MacsBug type 'hx syszone^' or just 'hx' to 
  470. set the current heap to the system heap (where your extension is 
  471. located).  Type 'br ' (don't hit return yet) and then type cmd-D to 
  472. view the names of all the routines in the system heap that were 
  473. compiled with MacsBug names turned on (like yours, right?)  Scroll 
  474. down until you find the name of your routine.  Hit enter and the 
  475. command line should look like 'br yourRoutineName'.  Hit enter again 
  476. and the breakpoint will be set.  You can also simply type 'br 
  477. yourRoutineName' and MacsBug will find it for you.  The zone must be 
  478. set to the zone containing the routine that you want to break on for 
  479. MacsBug to find it, so make sure to set the zone to the system zone.
  480.  
  481. Identifying your extension in the system heap is dependent on 
  482. compiling it with the MacsBug symbols option turned on.  This inserts 
  483. Ascii versions of the names of each function in the compiled code in 
  484. such a way the MacsBug , and other debuggers can find these names.  
  485. Jasik's debugger uses a different method for identifying your code 
  486. that is based on SYM files, which are generated by your compiler.  
  487. The SYM files allows Jasik's debugger to do source level debugging of 
  488. INITs and other code resources.
  489.  
  490. I have used a two-project method, when developing INITs and other 
  491. code resources, that helps to cut down debugging time.  Any extension 
  492. consists of essentially two parts: the initialization portion and the 
  493. implementation portion.  The initialization portion detaches the 
  494. resource as described above, shows the icon, and often patches traps.  
  495. The implementation portion contains the actual trap patches.  It is 
  496. quite possible, and desirable, to test the implementation portion in 
  497. the context of an application, rather than as an INIT.  To accomplish 
  498. this your project consists of three parts, which for a simple case 
  499. would be three files.
  500.  
  501. TesterApp Project:
  502.    SetUpApplication.c   Simple application shell that sets up 
  503.                the trap patches and provides a  
  504.                mechanism to call the traps
  505.    TrapPatches.c     Code for the trap patches
  506.  
  507. INIT Project:
  508.    SetUpINIt.c       Standard INIT setup; contains main  
  509.                entry point; patches all necessary  
  510.                traps
  511.    TrapPatches.c     Code for the trap patches; same file as 
  512.                in the TesterApp Project
  513.  
  514. The mechanism for calling the traps in SetUpApplication.c is usually 
  515. a menu item.  In some cases it might be a dialog with several 
  516. buttons, each of which calls a particular trap.  It isn't always 
  517. necessary to patch traps in your TesterApplication.  Simply calling 
  518. the routines that implement the guts of the trap patches will often 
  519. be just as good.  Having two projects, one that builds the tester 
  520. application and the other that builds the extension, allows you to 
  521. save time debugging and to be able to build the extension at any 
  522. time.
  523.  
  524. Writing and testing extensions involves multiple rounds of 
  525. 'compiling-installing the INIT-rebooting-Stepping through the INIT in 
  526. a low level debugger'.  Use of the two-project method will cut down 
  527. this time.
  528.  
  529. --
  530.  
  531. --------------------comp.sys.mac.programmer.info---------------------
  532. comp.sys.mac.programmer.info  is  primarily  for  distributing  FAQs,
  533. tutorials, news, and  similar  documents  related to programming  the
  534. Macintosh.  To post, email csmp_info@xplain.com
  535. -----------------------about MacTech Magazine----------------------
  536. PO Box 250055, Los Angeles, CA 90025, 310-575-4343, Fax:310-575-0925
  537. For more info, anonymous ftp to ftp.netcom.com and cd to /pub/xplain
  538. or email to the following @xplain.com : custservice, editorial, 
  539. adsales, marketing, accounting, pressreleases, progchallenge, 
  540. publisher, info
  541.